/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package compilador.AnalisadorLexico;

/**
 *
 * @author Leles
 */

/* Coisas do java que vamos usar pra tratar a abertura do arquivo e a leitura do mesmo*/
import compilador.AnalisadorLexico.Atomo.TokenType;
import java.io.FileReader;
import java.io.IOException;



public class Lexico {
/* A variavel do arquivo e a sequencia de caracteres a serem lidos*/
 String inputFile;
 FileReader charStream;



private enum EstadosLex {
    ESTADO_INICIAL,
    ID, NUMERO, STRING,
    PARENTESES_ABRE, PARENTESES_FECHA, PONTOEVIRGULA,
    OP_ALG_MAIS, OP_ALG_MENOS, OP_ALG_MULT, DOISPONTOS,
    OP_BOOL_IGUAL, OP_BOOL_DIF, OP_BOOL_MAIOR_MAIORIGUAL, OP_BOOL_MENOR_MENORIGUAL,
    OP_BOOL_E, OP_BOOL_OU,
    BARRA, COMENTARIO_LINHA, COMENTARIO_BLOCO, FIM_COMENTARIO_BLOCO,
    ACEITA



}

private int valor_inicial_col = 0;
private int caractere = -10;
private int linha = 1;
private int col = valor_inicial_col;

private int Char_Le (){
    if (caractere == -10)
        Char_Avanca();
    //System.out.println(String.valueOf((char) caractere));
    return caractere;
}

private void Char_Avanca (){
    try { 
        boolean rf_fr = false;
        if (caractere == '\f' || caractere == '\r')
            rf_fr = true;
        caractere = charStream.read();
        switch (caractere){
            case '\n': col = valor_inicial_col; linha++; break;
            case '\f': 
            case '\r':
                if (rf_fr == true) {
                    col = 1;
                    linha++; 
                }
                break;
            case '\t':
                col += 8;
                break;
            case ' ': 
            default: 
                col++;
                break;
        }
    }
    catch(IOException e){
        System.out.println("Exception e: " + e.getMessage()); // Se não conseguir ler avisa o porque
    }
}

private String Char_Agrega(String token){
    return token + (char) caractere;
}

public String getPosicaoCodigo (){
    return  "Linha " + linha + ", coluna " + col;
}

private TokenType PalavraReservada (String token){
    for (Object t[]: Atomo.PalavrasReservadas){
        if(t[0].equals(token))
            return (TokenType) t[1];
    }
        
    for (Object t: Atomo.PalavrasReservadas_Tipos){
        if(t.equals(token))
            return TokenType.TIPO;
    }
    
    return TokenType.ID;
}
private boolean SegueNumeroSemEspaco(int x){
    switch(x){ 
        case ')': 
        case ';': 
        case ':': 
        case '+':
        case '-': 
        case '*':
        case '/': 
        case '=':
        case '!': 
        case '>':
        case '<':
        case '&':
        case '|': 
        case ' ': 
        case '\t':
        case '\n':
        case '\f':
        case '\r': return true;
        default: return false;
    }    
}

public Atomo ProximoAtomo (){
    EstadosLex estado = EstadosLex.ESTADO_INICIAL;
    String token = "";      // COMO MEXER COM STRING?? TEM JEITO BOM DE JUNTAR CHAR NO FIM DE STRING SEM RECOPIAR TUDO?
    Atomo atomo = new Atomo();
    
    do {
        switch (estado){
            case ESTADO_INICIAL: 
                if (Character.isLetter(Char_Le()) || Char_Le() == '_'){
                    token = Char_Agrega(token);
                    Char_Avanca();
                    estado = EstadosLex.ID;
                }
                else if (Character.isDigit(Char_Le())) {
                    token = Char_Agrega(token);
                    Char_Avanca();
                    estado = EstadosLex.NUMERO;
                }
                else
                    switch (Char_Le()){
                        case '"': estado = EstadosLex.STRING;                                      Char_Avanca() ; break;
                        case '(': estado = EstadosLex.PARENTESES_ABRE; token = Char_Agrega(token); Char_Avanca() ; break;
                        case ')': estado = EstadosLex.PARENTESES_FECHA;    token = Char_Agrega(token); Char_Avanca() ; break;
                        case ';': estado = EstadosLex.PONTOEVIRGULA;   token = Char_Agrega(token); Char_Avanca() ; break;
                        case '+': estado = EstadosLex.OP_ALG_MAIS ;    token = Char_Agrega(token); Char_Avanca(); break;
                        case '-': estado = EstadosLex.OP_ALG_MENOS ;   token = Char_Agrega(token); Char_Avanca(); break;
                        case '*': estado = EstadosLex.OP_ALG_MULT ;    token = Char_Agrega(token); Char_Avanca(); break;
                        case '/': estado = EstadosLex.BARRA ;          token = Char_Agrega(token); Char_Avanca(); break;
                        case ':': estado = EstadosLex.DOISPONTOS ;  token = Char_Agrega(token); Char_Avanca(); break;
                        case '=': estado = EstadosLex.OP_BOOL_IGUAL ;  token = Char_Agrega(token); Char_Avanca(); break;
                        case '!': estado = EstadosLex.OP_BOOL_DIF ;    token = Char_Agrega(token); Char_Avanca(); break;
                        case '>': estado = EstadosLex.OP_BOOL_MAIOR_MAIORIGUAL ; token = Char_Agrega(token); Char_Avanca(); break;
                        case '<': estado = EstadosLex.OP_BOOL_MENOR_MENORIGUAL ; token = Char_Agrega(token); Char_Avanca(); break;
                        case '&': estado = EstadosLex.OP_BOOL_E ;      token = Char_Agrega(token); Char_Avanca(); break;
                        case '|': estado = EstadosLex.OP_BOOL_OU ;     token = Char_Agrega(token); Char_Avanca(); break;
                        case ' ': Char_Avanca(); break;
                        case '\t': Char_Avanca(); break; 
                        case '\n': Char_Avanca(); break;
                        case '\f': Char_Avanca(); break;
                        case '\r': Char_Avanca(); break;
                        case -1: atomo.tipo = TokenType.FIM_ARQUIVO; return atomo;
                        default: throw new RuntimeException ("Erro na analise LEXICA: Token desconhecido\nLinha " + linha + ", coluna " + col + ": sequencia ilegal" );
                    }
                break;
            case ID:
                if (Character.isLetter(Char_Le()) || Character.isDigit(Char_Le()) || Char_Le() == '_') {
                    estado = EstadosLex.ID;
                    token = Char_Agrega(token);
                    Char_Avanca();
                } 
                else { //if  (SegueAtomoSemEspaco(Char_Le())){
                    atomo.tipo = PalavraReservada(token);
                    if (atomo.tipo == TokenType.ID)
                        // *************************************************************************
                        atomo.valor =  token;
                    else
                        atomo.valor = token;
                    estado = EstadosLex.ACEITA;
                }
                // else throw new RuntimeException ("Erro na analise LEXICA: ID " + token + ", linha " + linha + ", coluna " + col + ": sequencia ilegal" );
                break; 
            case NUMERO:
                if (Character.isDigit(Char_Le())) {
                    estado = EstadosLex.NUMERO;
                    token = Char_Agrega(token);
                    Char_Avanca();
                } 
                // simplesmente achar o fim do arquivo pela inexistência de um digito na sequência 
                else if (SegueNumeroSemEspaco(Char_Le())){
                    atomo.tipo = TokenType.NUMERO;
                    atomo.valor = token;
                    estado = EstadosLex.ACEITA; 
                }
                else{
                    throw new RuntimeException ("Erro na analise LEXICA: Numero " + token + "\nLinha " + linha + ", coluna " + col + ": sequencia ilegal" );
                }
                break;
            case STRING:
                if (Char_Le()=='\"'){
                    atomo.tipo = TokenType.STRING;
                    atomo.valor = token;
                    Char_Avanca();
                    estado = EstadosLex.ACEITA; 
                }
                else {
                    switch (Char_Le()){
                        case '\t': 
                        case '\n': 
                        case '\f': 
                        case '\r': 
                        case -1: 
                            throw new RuntimeException (
                                    "Erro na analise LEXICA: String " + token + "\n" +
                                    "Linha " + linha + ", coluna " + col + ": sequencia ilegal. Proibidos tab e salto de linha em strings. Usar caracteres de escape (ainda não implementados. Chore)" );
                        default: 
                            token = Char_Agrega(token);
                            Char_Avanca();
                            estado = EstadosLex.STRING;
                            break;
                    }
                }
                break;
            case PARENTESES_ABRE:
                atomo.tipo = TokenType.PARENTESES_ABRE;
                atomo.valor = token;
                estado = EstadosLex.ACEITA;
                break;
            case PARENTESES_FECHA:
                atomo.tipo = TokenType.PARENTESES_FECHA;
                atomo.valor = token;
                estado = EstadosLex.ACEITA;
                break;
            case PONTOEVIRGULA:
                atomo.tipo = TokenType.PONTOEVIRGULA;
                atomo.valor = token;
                estado = EstadosLex.ACEITA;
                break;
            case OP_ALG_MAIS:
                atomo.tipo = TokenType.OP_ALG_MAIS;
                atomo.valor = token;
                estado = EstadosLex.ACEITA;
                break;
            case OP_ALG_MENOS:
                atomo.tipo = TokenType.OP_ALG_MENOS;
                atomo.valor = token;
                estado = EstadosLex.ACEITA;
                break;
            case OP_ALG_MULT:
                atomo.tipo = TokenType.OP_ALG_MULT;
                atomo.valor = token;
                estado = EstadosLex.ACEITA;
                break;
            case DOISPONTOS:
                if (Char_Le() == '='){
                    token = Char_Agrega(token);
                    Char_Avanca();
                    atomo.tipo = TokenType.OP_ATRIBUICAO;
                    atomo.valor = token;
                    estado = EstadosLex.ACEITA;
                }
                else {
                    atomo.tipo = TokenType.DOISPOINTOS;
                    atomo.valor = token;
                    estado = EstadosLex.ACEITA;
                }
                break;
            case OP_BOOL_IGUAL:
                if (Char_Le() == '='){
                    token = Char_Agrega(token);
                    Char_Avanca();
                    atomo.tipo = TokenType.OP_BOOL_IGUAL;
                    atomo.valor = token;
                    estado = EstadosLex.ACEITA;
                }
                else
                    throw new RuntimeException ("Erro na analise LEXICA: Igualdade (ou atribuição?)\nLinha " + linha + ", coluna " + col + ": sequencia ilegal" );
                break;
            case OP_BOOL_DIF:
                if (Char_Le() == '='){
                    token = Char_Agrega(token);
                    Char_Avanca();
                    atomo.tipo = TokenType.OP_BOOL_DIF;
                    atomo.valor = token;
                    estado = EstadosLex.ACEITA;
                }
                else
                    throw new RuntimeException ("Erro na analise LEXICA: Desigualdade\nLinha " + linha + ", coluna " + col + ": sequencia ilegal" );
                break;
            case OP_BOOL_MAIOR_MAIORIGUAL:
                if (Char_Le() == '='){
                    token = Char_Agrega(token);
                    Char_Avanca();
                    atomo.tipo = TokenType.OP_BOOL_MAIORIGUAL;
                    atomo.valor = token;
                    estado = EstadosLex.ACEITA;
                }
                else {
                    atomo.tipo = TokenType.OP_BOOL_MAIOR;
                    atomo.valor = token;
                    estado = EstadosLex.ACEITA;
                }
                break;
            case OP_BOOL_MENOR_MENORIGUAL:
                if (Char_Le() == '='){
                    token = Char_Agrega(token);
                    Char_Avanca();
                    atomo.tipo = TokenType.OP_BOOL_MENORIGUAL;
                    atomo.valor = token;
                    estado = EstadosLex.ACEITA;
                }
                else {
                    atomo.tipo = TokenType.OP_BOOL_MENOR;
                    atomo.valor = token;
                    estado = EstadosLex.ACEITA;
                }
                break;
            case OP_BOOL_E:
                if (Char_Le() == '&'){
                    token = Char_Agrega(token);
                    Char_Avanca();
                    atomo.tipo = TokenType.OP_BOOL_E;
                    atomo.valor = token;
                    estado = EstadosLex.ACEITA;
                }
                else
                    throw new RuntimeException ("Erro na analise LEXICA: E logico\nLinha " + linha + ", coluna " + col + ": sequencia ilegal" );
                break;
            case OP_BOOL_OU:
                if (Char_Le() == '|'){
                    token = Char_Agrega(token);
                    Char_Avanca();
                    atomo.tipo = TokenType.OP_BOOL_OU;
                    atomo.valor = token;
                    estado = EstadosLex.ACEITA;
                }
                else
                    throw new RuntimeException ("Erro na analise LEXICA: OU logico\nLinha " + linha + ", coluna " + col + ": sequencia ilegal" );
                break;
            case BARRA:
                if (Char_Le() == '/'){
                    token = "";
                    Char_Avanca();
                    estado = EstadosLex.COMENTARIO_LINHA;
                }
                else if (Char_Le() == '*'){
                    token = "";
                    Char_Avanca();
                    estado = EstadosLex.COMENTARIO_BLOCO;
                }
                else {
                    atomo.tipo = TokenType.OP_ALG_DIV;
                    atomo.valor = token;
                    estado = EstadosLex.ACEITA;
                }
                break;
            case COMENTARIO_LINHA:
                switch(Char_Le()){
                    case -1:
                    case '\n':
                    case '\f':
                    case '\r': estado = EstadosLex.ESTADO_INICIAL; break;
                    default:   break;
                }
                Char_Avanca();
                break;
            case COMENTARIO_BLOCO:
                if (Char_Le() == '*')
                    estado = EstadosLex.FIM_COMENTARIO_BLOCO;
                Char_Avanca();
                break;
            case FIM_COMENTARIO_BLOCO:
                if (Char_Le() == '/')
                    estado = EstadosLex.ESTADO_INICIAL;
                Char_Avanca();
                break;
            default:
                throw new RuntimeException ("Erro na analise LEXICA: Estado desconhecido\nLinha " + linha + ", coluna " + col + ": sequencia ilegal" );
        }
    } while (estado != EstadosLex.ACEITA);

    System.out.printf("%-25s","Tipo  = " + atomo.tipo);
    System.out.printf("%-20s\n","Valor  = " + atomo.valor);
    //System.out.println("Valor = " + atomo.valor);
    
    return atomo;
}


         
//Metodo que le o arquivo
    public Lexico(String inputfile){
         this.inputFile = inputfile; //Joga o arquivo na variavel
         try{
             charStream = new FileReader(inputFile); //Tenta ler
         } catch(IOException e){
               System.out.println("Exception e: " + e.getMessage()); // Se não conseguir ler avisa o porque
         }
         // TESTE
         // System.out.println(PalavraReservada("FUNCTION"));
         
    }
}

